package com.whale.kernel.service.hibernate;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import com.whale.kernel.domain.Authority;
import com.whale.kernel.domain.UserAccount;
import com.whale.kernel.service.UserService;

public class UserDetailsHibernateService extends AbstractHibernateService
		implements UserDetailsService, UserService {

	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException, DataAccessException {
		UserAccount user = findUserByName(username);
		if (user == null) {
			throw new UsernameNotFoundException("User not found: " + username);
		}

		List<Authority> authorities = findUserAuthorities(username);
		Collection<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
		for (Authority auth : authorities) {
			grantedAuthorities
					.add(new GrantedAuthorityImpl(auth.getAuthority()));
		}

		return new User(username, user.getPassword(), user.getEnabled(), true,
				true, true, grantedAuthorities);
	}

	public UserAccount findUserByName(String username) {
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("username", username);
		return this.getUniqueQueryResult(UserAccount.class,
				"from UserAccount where username =:username", params);
	}

	public List<Authority> findUserAuthorities(String username) {
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("username", username);
		return this.getQueryResult(Authority.class,
				"from Authority where userAccount.username =:username", params);
	}

	public void createOrUpdateUser(String username, String password,
			Collection<String> roles) {
		UserAccount userAccount = findUserByName(username);
		if (userAccount == null) {
			userAccount = new UserAccount();
			userAccount.setUsername(username);
			userAccount.setEnabled(true);
		}
		Md5PasswordEncoder md5Encoder = new Md5PasswordEncoder();
		userAccount.setPassword(md5Encoder.encodePassword(password, null));
		hibernateTemplate.saveOrUpdate(userAccount);
		
		// delete all roles for user
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("userAccount", userAccount);
		this.executeUpdate("delete from Authority where userAccount =:userAccount", params);
		
		// insert new user roles
		for (String role : roles) {
			Authority auth = new Authority();
			auth.setAuthority(role);
			auth.setUserAccount(userAccount);
			hibernateTemplate.save(auth);
		}
	}

	public UserAccount createUser(UserAccount userAccount,
			Collection<String> authorities) {
		Md5PasswordEncoder md5Encoder = new Md5PasswordEncoder();
		String password = userAccount.getPassword();
		String hashPasword = md5Encoder.encodePassword(password, null);
		userAccount.setPassword(hashPasword);
		userAccount.setConfirmPassword(hashPasword);
		hibernateTemplate.save(userAccount);
		
		UserAccount createdUser = findUserByName(userAccount.getUsername());
		// insert new user roles
		for (String role : authorities) {
			Authority auth = new Authority();
			auth.setAuthority(role);
			auth.setUserAccount(createdUser);
			hibernateTemplate.save(auth);
		}
		
		return createdUser;
	}

}
